package jam4j;

import java.io.Flushable;
import java.io.IOException;

/**
 * A kind of output that may be displayed. Each can be enabled or disabled by
 * command-line option or method of {@link Jam4J}.
 * 
 * @see Jam4J#display(jam4j.Jam4J.DisplayOption)
 * @see Jam4J#display(int, boolean)
 * @see Globals#enabled(OutputChannel)
 * 
 * @author Luke Maurer
 */
public enum OutputChannel implements Appendable, Flushable {
    /**
     * Prints the name and parameters of each action as it is executed. This is
     * the only channel that is enabled by default.
     */
    MAKE(1),
    /**
     * Prints the name and parameters of each action, even those with the
     * {@code quietly} modifier.
     * 
     * @see jam4j.lang.ActionDef.Modifier#QUIETLY
     */
    MAKE_QUIET(11),
    /**
     * Prints messages as each target is updated or determined to be up to
     * date.
     */
    MAKE_PROGRESS(3),
    EXEC(12),
    /**
     * Prints each update action (e.g. command line) in full before it is
     * executed.
     */
    EXEC_COMMAND(4),
    /**
     * Prints a detailed trace of script execution.
     */
    COMPILE(5),
    /**
     * Traces header scanning in detail.
     */
    SEARCH(6),
    /**
     * Displays each variable as it is set.
     */
    VAR_SET(7),
    /**
     * Displays each reference as it is evaluated.
     */
    VAR_GET(8),
    /**
     * Traces lexing and parsing (not used).
     */
    SCAN(9),
    /**
     * Prints all dependencies in Jam script format.
     */
    DEPENDS(13),
    /**
     * Traces decisions as to which targets are updated and the dependent
     * targets causing them to be updated.
     */
    CAUSES(14),
    ;
    
    public static final OutputChannel
        HEADER      = SEARCH,
        BIND_SCAN   = SEARCH,
        VAR_EXPAND  = VAR_GET,
        IF_EVAL     = VAR_GET,
        LISTS       = SCAN,
        MEMORY      = SCAN;
    
    private static final int DEBUG_MAX = 15;
    
    static final OutputChannel[] LEVELS = new OutputChannel[DEBUG_MAX];
    
    private final int level;
    private OutputChannel(int level) {
        this.level = level;
    }
    
    static {
        for (OutputChannel channel : values())
            LEVELS[channel.level] = channel;
    }
    
    /**
     * @return Whether this output channel is enabled. If disabled, the channel
     * outputs nothing.
     */
    public final boolean isEnabled() {
        return Globals.enabled(this);
    }

    /**
     * Output the given character sequence, if the channel is enabled.
     * 
     * @param csq The sequence to output.
     * 
     * @see #isEnabled()
     */
    public final OutputChannel append(CharSequence csq) throws IOException {
        if (isEnabled())
            Globals.out().append(csq);
        return this;
    }

    /**
     * Output the given character, if the channel is enabled.
     * 
     * @param c The character to output.
     * 
     * @see #isEnabled()
     */
    public final OutputChannel append(char c) throws IOException {
        if (isEnabled())
            Globals.out().append(c);
        return this;
    }

    /**
     * Output a subsequence of the given character sequence, if the channel is
     * enabled.
     * 
     * @param csq The sequence to output part of.
     * @param start The index at the beginning of the subsequence to output.
     * @param end The index after the end of the subsequence to output.
     * 
     * @see #isEnabled()
     */
    public final OutputChannel append(CharSequence csq, int start, int end)
            throws IOException {
        if (isEnabled())
            Globals.out().append(csq, start, end);
        return this;
    }
    
    /**
     * A convenience method to format a message to this output channel.
     * 
     * @param template The template for the message, in the format of
     *            {@link String#format(String, Object...)}.
     * @param args The arguments to the template.
     * @return This object, for chaining.
     * 
     * @see String#format(String, Object...)
     * @see #append(CharSequence)
     */
    public final OutputChannel printf(String template, Object ... args) {
        if (isEnabled())
            Globals.out().format(template, args);
        return this;
    }
    
    public final void flush() throws IOException {
        Globals.out().flush();
    }
}